﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using System.IO;
using System.Globalization;
using ptp3.Models;



namespace ptp3.Controllers
{

    public class AnnounceController : Controller
    {
        
        //
        // GET: /Announce/
        public string Index()
        {
            #region Decode_REQUESTS_hash
            string rawurl = Request.RawUrl;
            string info_hash = rawurl.Split('?', '&').
                Where(st => st.StartsWith("info_hash=")).First()
                .Remove(0, "info_hash=".Count());

            string peer_id = rawurl.Split('?', '&').
                Where(st => st.StartsWith("peer_id=")).First()
                .Remove(0, "peer_id=".Count());
            var ms = new MemoryStream();
            for (var i = 0; i < info_hash.Length; i++)
            {
                if (info_hash[i] == '%')
                {
                    ms.WriteByte(
                        byte.Parse(info_hash.Substring(i + 1, 2), NumberStyles.AllowHexSpecifier));
                    i += 2;
                }
                else if (info_hash[i] < 128)
                {
                    ms.WriteByte((byte)info_hash[i]);
                }
            }

            byte[] infoHash = ms.ToArray();
            info_hash = BitConverter.ToString(infoHash).Replace("-","");
            #endregion

            #region Validations

            if (info_hash == null)
                throw new BEException("No info_hash provided.");
            
            if (info_hash.Length != 40)
            {
                throw new BEException("Invalid info_hash length.");
            }
            if (peer_id.Length < 0)
            {
               throw new BEException("No peer_id provided.");
            }

            var xevent = Request["event"];

            var ip = Request.UserHostAddress;

            if (Request.QueryString["port"] == null || Request.QueryString["downloaded"] == null || Request.QueryString["uploaded"] == null || Request.QueryString["left"] == null)
            {
                throw new BEException("Invalid information received from BitTorrent client");
            }
            int numwant, port, left;
            try
            {
                numwant = Convert.ToInt32(Request.QueryString["numwant"]);
            }
            catch (Exception)
            {
                numwant = 50;
            }
            
            try
            {
                port = Convert.ToInt32(Request.QueryString["port"]);
            }
            catch (Exception)
            {
                port = 0;
            }
            var nopeerid = Request.QueryString["no_peer_id"].Contains("no_peer_id") && (Request.QueryString["no_peer_id"] == "1");
            try
            {
                left = int.Parse(Request.QueryString["left"]);
            }
            catch (Exception)
            {
                left = 0;
            }

            #endregion

            ptp3Entities ptp3db = new ptp3Entities();

            #region Torrents
            var torrentList = ptp3db.torrents.Where(f => f.info_hash==info_hash);
            if (torrentList.Any() == false)
            {
                torrents torr = new torrents();
                torr.info_hash = info_hash;
                ptp3db.AddTotorrents(torr);
            }
            #endregion

            #region Peers
            var peersList = ptp3db.peers.Where(k => k.peer_id==peer_id && k.info_hash==info_hash);
            if (peersList.Any() == false)
            {
                peers peer = new peers();
                peer.info_hash = info_hash;
                peer.peer_id = peer_id;
                peer.compact = Request.QueryString["compact"];
                peer.port = port;
                peer.ip = ip;
                peer.no_peer_id = Request.QueryString["no_peer_id"];
                peer.key = Request.QueryString["key"];
                peer.downloaded = int.Parse(Request.QueryString["downloaded"]);
                peer.uploaded = int.Parse(Request.QueryString["uploaded"]);
                peer.left = int.Parse(Request.QueryString["left"]);

                peer.numwant = int.Parse(Request.QueryString["numwant"]);
                ptp3db.AddTopeers(peer);
            }
            else 
            {
                peers peer = peersList.SingleOrDefault();
                peer.downloaded = peer.downloaded + int.Parse(Request.QueryString["downloaded"]);
                peer.uploaded = peer.uploaded + int.Parse(Request.QueryString["uploaded"]);
                peer.port = port;
                peer.ip = ip;
                ptp3db.SaveChanges();
            }
            #endregion

            #region Users
            var usersList = ptp3db.users.Where(u => u.ip == ip);
            if (usersList.Any() == false)
            {
                users user = new users();
                user.ip = ip;
                user.downloaded = int.Parse(Request.QueryString["downloaded"]);
                user.uploaded = int.Parse(Request.QueryString["uploaded"]);
                DateTime time = DateTime.Now;              // Use current time
                string format = "yyyy-MM-dd HH:mm:ss";    // Use this format
                user.last_seen = DateTime.Parse(time.ToString(format));  // Write 
                user.announces = 0;
                ptp3db.AddTousers(user);
            }
            else
            {
                users user = usersList.SingleOrDefault();
                user.downloaded = user.downloaded + int.Parse(Request.QueryString["downloaded"]);
                user.uploaded = user.uploaded + int.Parse(Request.QueryString["uploaded"]);
                DateTime time = DateTime.Now;              // Use current time
                string format = "yyyy-MM-dd HH:mm:ss";    // Use this format
                user.last_seen = DateTime.Parse(time.ToString(format));  // Write
                user.announces++;
                ptp3db.SaveChanges();
            }
            #endregion


            ptp3db.SaveChanges();

            #region PeerHandeling
            var ThisTorrent = ptp3db.torrents.Where(t => t.info_hash == info_hash).SingleOrDefault();
            var ThisPeer = ptp3db.peers.Where(t => t.info_hash == info_hash && t.peer_id==peer_id).SingleOrDefault();
            
            
            var peers = new List<IBE>();
            lock (ThisPeer)
            {
                if (xevent == "stopped")
                {
                    if (ThisPeer !=null)
                    {
                        if (ThisPeer.left == 0)
                        {
                            ThisTorrent.seeding--;
                        }
                        else
                        {
                            ThisTorrent.leeching--;
                        }
                        ptp3db.DeleteObject(ThisPeer);
                    }
                }
                else
                {
                    if (ThisPeer!=null)
                    {
                        ThisPeer.ip = ip;
                        ThisPeer.port = port;
                        ThisPeer.left = left;
                    }
                    if (xevent=="started") {
                        if (left == 0)
                            ThisTorrent.seeding++;
                        else
                            ThisTorrent.leeching++;
                    }
                    if (xevent == "completed")
                    {
                        ThisTorrent.completed++;
                       
                    }
                    if (numwant > 0)
                    {
                        List <peers>rawpeers;
                        var peerList = ptp3db.peers.Where(t => t.info_hash==info_hash);
                        if (xevent == "completed" || left == 0)
                            rawpeers = peerList.OrderBy(x => x.left > 0).Take(numwant).ToList();
                        else
                            rawpeers = peerList.Where(x => x.left != 0).OrderBy(x => x.left).Take(numwant).ToList();
                        foreach (var rawpeer in rawpeers)
                        {
                            Dictionary<BEString, IBE> Peer = new Dictionary<BEString, IBE>();
                            if (nopeerid)
                                Peer["peer id"] = (BEString)rawpeer.peer_id;
                            Peer["ip"] = (BEString)rawpeer.ip;
                            Peer["port"] = (BENumber)rawpeer.port;
                            peers.Add((BEDict)Peer);
                        }
                    }
                }
            }
            ptp3db.SaveChanges();
            #endregion
            settings setting = ptp3db.settings.FirstOrDefault();
            BEDict response = new Dictionary<BEString, IBE> {
					{"complete", (BENumber)ThisTorrent.seeding},
					{"incomplete", (BENumber)ThisTorrent.leeching},
					{"interval", (BENumber)setting.interval},
					{"min interval", (BENumber)setting.min_interval},
					{"peers", (BEList)peers}
				};
                return response.Dump();
        }

        public class BEException : Exception
        {
            public BEException(string message)
                : base(message)
            {
            }
            public override string ToString()
            {
                BEDict dict = new Dictionary<BEString, IBE> { { "failure reason", (BEString)Message } };
                return dict.Dump();
            }
        }

    }
}
//+		Params	{
//info_hash=c%1eL%u038c%ufffd%ufffd%ufffdY%5d%2b%ufffd%ufffd%09%ufffd%ufffd%u047cy8
//&peer_id=-UT3120-%ufffdh-D%ufffd%ufffd4%ufffd%ufffd%3dI
//&port=14783
//&uploaded=143671296
//&downloaded=0
//&left=0
//&corrupt=0
//&key=DA33EDB6
//&event=started
//&numwant=200
//&compact=1
//&no_peer_id=1
//&ipv6=2001%3a0%3a5ef5%3a79fd%3a28ef%3a8cc%3aafc7%3ad8b8.

//RawUrl = "/announce?info_hash=%db%5b%98%c8%95%81%8e%ae%f0%bf%c7f%ad%23%bb%3aw%ae%d4%f3&peer_id=-UT3130-%d5h%b8%04%10%e1r%3b%aa%e3%de%c3&port=48399&uploaded=0&downloaded=0&left=0&corrupt=0&key=353DD8BE&event=started&numwant=200&compact=1&no_peer_id=1"

